package system.train; 

import java.util.LinkedList;
import system.track.Block;
import system.track.Switch;
import system.track.Direction;
import system.track.SwitchAngle;
import system.track.Sensor;
import system.track.IllegalFunctionCallException;

/**
 * This is an enumeration of possible actions a train may take.
 * 
 * @author Yasmine K
 * @version Cycle 1 - complete
 */
public enum RouteActions 
{
    /**
     * Sets the next Switch that the train will encounter to 180
     */
    SET_NEXT_SWITCH_180 
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            train.getCurrentBlock().getSwitch(currentDirection).setAngle(SwitchAngle.ONE_EIGHTY_DEGREES);
            return false;
        }
    },

    /**
     * Sets the next Switch that the train will encounter to 90
     */
    SET_NEXT_SWITCH_90
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            train.getCurrentBlock().getSwitch(currentDirection).setAngle(SwitchAngle.NINETY_DEGREES);
            return false;
        }
    },

    /**
     * Sets the next Block to "on" in preparation for the train's arrival.
     */
    ACTIVATE_NEXT_BLOCK 
    {
        boolean executeAction(Train train)
        {
            if (train.getCurrentBlock().getID() == 1 && train.getDirection() == Direction.COUNTERCLOCKWISE)
            {
                System.out.println("Break here.");
            }
            Direction currentDirection = train.getDirection();
            Block nextBlock = train.getCurrentBlock().getNextBlock(currentDirection);
            nextBlock.setDirection(currentDirection, train.getSpeed());
            System.out.println("currentBlock ID: " + train.getCurrentBlock().getID() + " nextBlock ID: " + nextBlock.getID());
            return false;
        }
    },

    /**
     * Sets the previous Block to "off" after the train has left it.
     * Only two Blocks may be active at a time.
     */
    DEACTIVATE_PREVIOUS_BLOCK 
    {
        boolean executeAction(Train train)
        {
            train.getCurrentBlock().setDirection(Direction.CLOCKWISE, 0);
            return false;
        }
    },

    /**
     * Move the train ahead to the next Block.
     */
    MOVE_TRAIN_AHEAD
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            Block nextBlock = train.getCurrentBlock().getNextBlock(currentDirection);
            System.out.println("Moving train to block: " + nextBlock.getID());
            train.setCurrentBlock(nextBlock);
            return false;
        }
    },

    /**
     * Activates the current Block.
     */
    ACTIVATE_CURRENT_BLOCK
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            train.getCurrentBlock().setDirection(currentDirection, train.getSpeed());
            return false;
        }
    },
    
    /**
     * Sets the parking Block to true; it can then divert a train into a parking area.
     */
    SET_BLOCK_TO_PARK 
    {
        boolean executeAction(Train train)
        {
            try
            {
                train.getCurrentBlock().setPark(true);
            }
            catch(IllegalFunctionCallException e) 
            {
            }
            return false;
        }
    },

    /**
     * Sets the parking Block to false; trains will go through without being diverted
     * to a parking area.
     */
    SET_BLOCK_NOT_TO_PARK 
    {
        boolean executeAction(Train train)
        {
            try
            {
                train.getCurrentBlock().setPark(false);
            }
            catch(IllegalFunctionCallException e) 
            {
            }
            return false;
        }
    },

    /**
     * Register upcoming Sensors to the train so they can be tripped.
     */
    REGISTER_SENSORS 
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            LinkedList<Sensor> sensors = train.getCurrentBlock().getSensors(currentDirection);
            for(Sensor asensor: sensors)
            {
                asensor.registerForNotification(train);
            }
            return false;
        }
    },

    /**
     * Unregister Sensors after the train has passed over them.
     */
    UNREGISTER_SENSORS 
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            LinkedList<Sensor> sensors = train.getCurrentBlock().getSensors(currentDirection);
            for(Sensor asensor: sensors)
            {
                asensor.unregisterForNotification(train);
            }
            return false;
        }
    },

    /**
     * Toggle the train's direction (the physical train can move backwards and
     * forwards with no difficulty).
     */
    TOGGLE_DIRECTION
    {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            switch(currentDirection)
            {
                case CLOCKWISE:
                    train.setDirection(Direction.COUNTERCLOCKWISE);
                    break;
                case COUNTERCLOCKWISE:
                    train.setDirection(Direction.CLOCKWISE);
                    break;
            }
            return false;
        } 
    },

    /**
     * Wait for the registered Sensors to be tripped before performing 
     * any other action.
     */
    WAIT_FOR_SENSOR 
    {
        boolean executeAction(Train train)
        {
            return true;
        }
    },

    /**
     * Stop the train (sets the speed to zero).
     */
    STOP 
    {
        boolean executeAction(Train train)
        {
            train.getCurrentBlock().setDirection(Direction.CLOCKWISE,0);
            train.getCurrentBlock().getNextBlock(train.getDirection()).setDirection(Direction.CLOCKWISE, 0);
            return false;
        }
    },

    /**
     * Starts the train by setting direction and speed.
     */
    START
    {
        boolean executeAction(Train train)
        {
            train.getCurrentBlock().setDirection(train.getDirection(), train.getSpeed());
            return false;
        }
    },

    /**
     * Start the current Block that the train is on and activate the next Block
     * on the train's Route.
     */
    CONTINUE
    {
        boolean executeAction(Train train)
        {
            RouteActions.START.executeAction(train);
            RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(train);
            return false;
        }
    },

    /**
     * Activate the parking Block; this requires special settings.
     */
    ACTIVATE_PARKING_BLOCK
    {
        boolean executeAction(Train train)
        {
            boolean returnVal = false;
            Direction d = train.getDirection();
            Block nextBlock = train.getCurrentBlock().getNextBlock(d);
            if (nextBlock.getID() == 4)
            {
                switch(d)
                {
                    case CLOCKWISE:
                        nextBlock.setDirection(Direction.COUNTERCLOCKWISE, train.getSpeed());
                        break;
                    case COUNTERCLOCKWISE:
                        nextBlock.setDirection(Direction.CLOCKWISE, train.getSpeed());
                        break;
                    default:
                }
            }
            else
            {
                returnVal = RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(train);
            }
            return returnVal;
        }
    };

    /**
     * Executes the chosen action, given a particular train.
     */
    abstract boolean executeAction(Train train);
}
